﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Diagnostics;

namespace Lilium.RetailAudit.Data
{
	public abstract class EntityBase<TEntity> : ICloneable<TEntity>, IEquatable<TEntity>, INotifyPropertyChanged // , IEditableObject
		where TEntity : EntityBase<TEntity>
	{
		static EntityBase()
		{
			var entityType = typeof(TEntity);
			var entityAttribute = entityType.GetCustomAttributes(typeof(EntityAttribute), true).Cast<EntityAttribute>().SingleOrDefault();
			if (entityAttribute != null)
			{
				if (!string.IsNullOrEmpty(entityAttribute.EntityNameField))
				{
					var context = new { EntityNameProperty = entityType.GetProperty(entityAttribute.EntityNameField), };
					m_GetEntityNameFunc = entity =>
					{
						var value = context.EntityNameProperty.GetValue(entity, null);
						return value != null ? value.ToString() : string.Empty;
					};
				}
			}
		}

		public static TEntity GetDefaultInstance()
		{
			return Activator.CreateInstance<TEntity>();
		}

		public override bool Equals(object obj)
		{
			return Equals(obj as TEntity);
		}

		public override int GetHashCode()
		{
			return EntityComparer<TEntity>.Default.GetHashCode((TEntity)this);
		}

		public override string ToString()
		{
			return GetEntityNameFunc != null
				? GetEntityNameFunc((TEntity)this)
				: base.ToString();
		}

		public void OnInsert()
		{
			if (CreationDate == null)
				CreationDate = DateTime.UtcNow;
		}

		public void OnUpdate()
		{
			if (LastEditDate == null)
				LastEditDate = DateTime.UtcNow;
		}

		#region public DateTime CreationDate

		private DateTime? m_CreationDate;

		public DateTime? CreationDate
		{
			[DebuggerNonUserCodeAttribute]
			get
			{
				return m_CreationDate;
			}
			[DebuggerNonUserCodeAttribute]
			set
			{
				if (m_CreationDate != value)
				{
					m_CreationDate = value;
					NotifyPropertyChanged("CreationDate");
				}
			}
		}

		#endregion

		#region public DateTime? LastEditDate

		private DateTime? m_LastEditDate;

		public DateTime? LastEditDate
		{
			[DebuggerNonUserCodeAttribute]
			get
			{
				return m_LastEditDate;
			}
			[DebuggerNonUserCodeAttribute]
			set
			{
				if (m_LastEditDate != value)
				{
					m_LastEditDate = value;
					NotifyPropertyChanged("LastEditDate");
				}
			}
		}

		#endregion

		#region ICloneable Members

		object ICloneable.Clone()
		{
			return EntityCloner<TEntity>.Default.Clone((TEntity)this);
		}

		#endregion

		#region ICloneable<TEntity> Members

		public TEntity Clone()
		{
			return EntityCloner<TEntity>.Default.Clone((TEntity)this);
		}

		#endregion

		#region IEquatable<TEntity> Members

		public bool Equals(TEntity other)
		{
			return EntityComparer<TEntity>.Default.Equals((TEntity)this, other);
		}

		#endregion

		#region INotifyPropertyChanged Members

		public event PropertyChangedEventHandler PropertyChanged;

		protected void NotifyPropertyChanged(string propertyName)
		{
			var propertyChanged = PropertyChanged;
			if (propertyChanged != null)
				propertyChanged(this, new PropertyChangedEventArgs(propertyName));
		}

		#endregion

		#region IEditableObject Members
		/*
		private TEntity OriginalState;

		public void BeginEdit()
		{
			if (OriginalState == null)
				OriginalState = this.Clone();
		}

		public void CancelEdit()
		{
			if (OriginalState != null)
				EntityCloner<TEntity>.Default.Assign(OriginalState, (TEntity)this);
			OriginalState = null;
		}

		public void EndEdit()
		{
			OriginalState = null;
		}
		*/
		#endregion

		#region private static Func<TEntity, string> GetEntityNameFunc

		private readonly static Func<TEntity, string> m_GetEntityNameFunc;

		private static Func<TEntity, string> GetEntityNameFunc
		{
			[DebuggerNonUserCodeAttribute]
			get
			{
				return m_GetEntityNameFunc;
			}
		}

		#endregion
	}
}